/* Copyright(C) 2006-2008 Dave Sexton  *
 * http://www.codeplex.com/aip         *
 * http://www.codeplex.com/aip/license *
 ***************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Web.SessionState;

namespace DaveSexton.Web.Controls
{
	/// <summary>
	/// Handles requests for AIP images being persisted on the server.
	/// </summary>
	/// <remarks>
	/// <para>
	/// This handler must be registered in a web site's configuration file before AIP can be used.  (See the example.)
	/// </para>
	/// <para>
	/// The <see cref="AutoInputProtectionControl"/> control assigns the <see cref="System.Web.UI.WebControls.Image.ImageUrl">ImageUrl</see> property
	/// of its <see cref="System.Web.UI.WebControls.Image">Image</see> control to a value that will cause the client to request
	/// the image from the server that was generated for the previous request.  The URL consists of a path that ASP.NET routes to this virtual request 
	/// handler and a public key in the query string that is used to identify the image on the server.
	/// </para>
	/// </remarks>
	/// <example>
	/// <para>
	/// The following example shows how <see cref="AutoInputProtectionRequestHandler"/> can be registerd in a web.config file.
	/// </para>
	/// <include file='examples.xml' path='//example[@id="handler"]/*'/>
	/// </example>
	/// <seealso cref="AutoInputProtectionControl.AssignImageUrl"/>
	/// <seealso cref="AutoInputProtectionSessionRequestHandler"/>
	public class AutoInputProtectionRequestHandler : IHttpHandler
	{
		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="AutoInputProtectionRequestHandler" /> class.
		/// </summary>
		public AutoInputProtectionRequestHandler()
		{
		}
		#endregion

		#region IHttpHandler Members
		/// <summary>
		/// Gets whether the handler is reusable.
		/// </summary>
		/// <value>Indicates whether the handler can be reused.  <see langword="true">True</see> is always returned.</value>
		public bool IsReusable
		{
			get { return true; }
		}

		/// <summary>
		/// Processes a web request in the specified <paramref name="context"/>.
		/// </summary>
		/// <remarks>
		/// <para>
		/// This method handles requests by retrieving an image from the cache, identified by a public key that is specified as 
		/// a query string argument, and streams it to the client.  The content-type header is set to, <em>image/jpg</em>.
		/// </para>
		/// <para>
		/// If the image is not configured to be persisted across multiple requests then the persisted memory stream is disposed before 
		/// returning to the caller.
		/// </para>
		/// <para>
		/// If an image is not found for the specified public key then a default image is returned that indicates to the user that the 
		/// requestd image does not exist.
		/// </para>
		/// </remarks>
		/// <exception cref="ArgumentNullException">The specified <paramref name="context"/> is <see langword="null"/>.</exception>
		/// <param name="context">The <see cref="HttpContext"/> of the current request.</param>
		public void ProcessRequest(HttpContext context)
		{
			if (context == null)
				throw new ArgumentNullException("context");

			string key = context.Request.QueryString["k"];

			bool keepAlive = false, missingSession = false;
			MemoryStream stream = null;

			try
			{
				stream = AutoInputProtection.GetCachedImageStream(context, key, out keepAlive);
			}
			catch (SessionStateRequiredException)
			{
				missingSession = true;
			}

			try
			{
				context.Response.ContentType = "image/jpg";

				if (stream != null)
					stream.WriteTo(context.Response.OutputStream);
				else
				{
#if DEBUG
					if (missingSession)
						context.Trace.Write("AIP Request Handler", "Session state required: " + key);
					else
						context.Trace.Write("AIP Request Handler", "Timeout expired: " + key);
#endif

					using (Bitmap bitmap = new Bitmap(150, 50))
					{
						using (Graphics graphics = Graphics.FromImage(bitmap))
						{
							graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
							graphics.FillRectangle(Brushes.White, 0, 0, 150, 50);
							graphics.DrawRectangle(Pens.Black, 0, 0, 149, 49);
							graphics.DrawString((missingSession) ? Resources.Text.AIP_SessionRequired : Resources.Text.AIP_ImageNotFoundInCache, new Font("Arial", 12), Brushes.Red,
								new RectangleF(5, 5, 140, 40), StringFormat.GenericDefault);
						}

						bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
					}
				}
			}
			finally
			{
				if (!keepAlive && stream != null)
					stream.Dispose();
			}
		}
		#endregion
	}
}
